Docker 构建镜像
Dockerfile 是什么
Dockerfile 其实就是我们用来构建 Docker 镜像的源码,当然这不是所谓的编程源码,而是一些命令的组合,只要理解它的逻辑和语法格式,就可以编写 Dockerfile 了。
简单点说,Dockerfile 的作用:它可以让用户个性化定制 Docker 镜像。因为工作环境中的需求各式各样,网络上的镜像很难满足实际的需求。
说白了 Dockerfile 就是一个用来构建镜像的文本文件(就是一些打包脚本)
语法:
# -f 自定义的 dockerfile 文件,-t 生成的文件名,最后的 . 表示生成在当前目录下
docker build -f dockerfile01 -t "testimage" .
构建的工作原理
docker build -t kubia .
用户告诉 Docker 需要基于当前目录(注意这里结尾的点)构建一个叫 kubia 的镜像,Docker 会在目录中寻找 Dockerfile,然后构建镜像

镜像构建原理与上下文
如果注意,会看到 docker build 命令最后有一个 .。这个 . 表示当前目录,这个路径并不止是在指定 Dockerfile 所在路径,而是指上下文路径
首先我们要理解 docker build 的工作原理。Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 Docker Remote API。
而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。
构建过程不是由 Docker 客户端进行的,而是将整个目录的文件上传到 Docker 守护进程并在那里进行的。
当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、ADD 指令等。而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?
这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路 径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。
如果在 Dockerfile 中这么写:
COPY ./package.json /app/
这并不是要复制执行 docker build 命令所在的目录下的 package.json,也不是复制 Dockerfile 所在目录下的 package.json,而是复制 上下文(context) 目录下的 package.json。
因此,COPY 这类指令中的源文件的路径都是相对路径。这也是为什么 COPY ../package.json /app 或者 COPY /opt/xxxx /app 无法工作的原因,因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。如果真的需要那些文件,应该将它们复制到上下文目录中去。
将 Dockerfile 放到了硬盘根目录去构建,结果发现 docker build 执行后,在发送一个几十 GB 的东西,极为缓慢而且很容易构建失败。那是因为这种做法是在让 docker build 打包整个硬盘,这显然是使用错误。
一般来说,应该会将 Dockerfile 置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 .gitignore 一样的语法写一个 .dockerignore,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。
Dockerfile 常用命令

# 先标明基础镜像
FROM ubuntu
VOLUME ["volume01","volume02"]
CMD echo "生成成功!"
CMD /bin/bash
RUN 命令指令通常用于安装应用和软件包。一个 Dockerfile 中可以有许多个 RUN 命令。
# apt-get update 和 apt-get install 被放在一个 RUN 指令中执行,这样能够保证每次安装的是最新的包。
RUN apt-get update && apt-get install -y \
bzr \
cvs \
git \
mercurial \
subversion
值得注意的是,一个 RUN 等效于一个 Shell,所以在执行下一个 RUN 时,上一个 RUN 的进程都已经结束了
CMD 命令
CMD 指令允许用户指定容器的默认执行的命令。此命令会在容器启动且 docker run 没有指定其他命令时运行。
即通过执行 docker run $image $other_command 启动镜像可以重载 CMD 命令。
下面是一个例子:
CMD echo "Hello world"
运行容器 docker run -it [image] 将输出:
Hello world
但当后面加上一个命令,比如 docker run -it [image] /bin/bash,CMD 会被忽略掉,命令 bash 将被执行:
Dockerfile 中只能有一条 CMD 命令,如果写了多条则最后一条生效,且 CMD 不支持接收 docker run 的参数。
ENTRYPOINT 入口程序
ENTRYPOINT 的 Exec 格式用于设置容器启动时要执行的命令及其参数,同时可通过CMD命令或者命令行参数提供额外的参数。
ENTRYPOINT 中的参数始终会被使用,这是与CMD命令不同的一点。下面是一个例子:
ENTRYPOINT ["/bin/echo", "Hello"]
当容器通过 docker run -it [image] 启动时,输出为:
Hello
而如果通过 docker run -it [image] CloudMan 启动,则输出为:
Hello CloudMan
将 Dockerfile 修改为:
ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["world"]
当容器通过 docker run -it [image] 启动时,输出为:
Hello world